package cn.disicn.model;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Page;

import cn.disicn.common.Const;
import cn.disicn.common.ServerResponse;
import cn.disicn.model.base.BaseSecPublishRefundInfo;
import cn.disicn.util.DateTimeUtil;
import cn.disicn.util.PageSource;
import cn.disicn.util.PayCommonUtil;
import cn.disicn.util.RefundRequest;
import cn.disicn.util.XMLUtil;
import cn.disicn.vo.RefundInfoVo;

/**
 * Generated by JFinal.
 */
@SuppressWarnings("serial")
public class SecPublishRefundInfo extends BaseSecPublishRefundInfo<SecPublishRefundInfo> {

	public static final SecPublishRefundInfo dao = new SecPublishRefundInfo().dao();

	private static final Logger logger = LoggerFactory.getLogger(SecPublishRefundInfo.class);

	/**
	 * 获取退款记录
	 * 
	 * @param pageNum
	 * @param pageSize
	 * @param status
	 * @param phone
	 * @return
	 */
	public PageSource<RefundInfoVo> getRefundApplication(Integer pageNum, Integer pageSize, String status,
			String phone) {
		StringBuilder sql = new StringBuilder("from sec_publish_refund_info where 1=1 ");
		if (status != null && status.trim() != "") {
			sql.append(" and status = " + status);
		}
		if (phone != null && phone.trim().length() > 0) {
			sql.append(" and userPhone = " + phone);
		}
		Page<SecPublishRefundInfo> page = dao.paginate(pageNum, pageSize, "select *", sql.toString());
		return new PageSource<RefundInfoVo>(page.getPageNumber(), page.getPageSize(), page.getTotalPage(),
				page.getTotalRow(), assembleRefundInfoVo(page.getList()));
	}

	/**
	 * 封装退款记录vo
	 * 
	 * @return
	 */
	private List<RefundInfoVo> assembleRefundInfoVo(List<SecPublishRefundInfo> list) {
		List<RefundInfoVo> refundList = Lists.newArrayList();

		// 查看任务详情
		String jobDetails = "<button title='查看任务详情' class='layui-btn layui-btn-radius layui-btn-sm layui-btn-normal check-job-btn'>查看任务详情</button>";
		// 确认退款按钮
		String refundsBtn = "<button title='点击退款' class='layui-btn layui-btn-radius layui-btn-sm layui-btn-danger refund-btn'>确认退款</button>";

		for (SecPublishRefundInfo refundInfo : list) {
			RefundInfoVo refundInfoVo = new RefundInfoVo();
			refundInfoVo.setId(refundInfo.getId());
			refundInfoVo.setUserId(refundInfo.getPublishUserId());
			refundInfoVo.setPhone(refundInfo.getUserPhone());
			refundInfoVo.setAmount(refundInfo.getAmount().toString());
			refundInfoVo.setJobId(refundInfo.getJobId());
			refundInfoVo.setStatus(Const.RefundStatusEnum.codeOf(Integer.parseInt(refundInfo.getStatus())).getValue());
			refundInfoVo
					.setPayPlatform(Const.PayTypeEnum.codeOf(Integer.parseInt(refundInfo.getPayPlatform())).getValue());
			refundInfoVo.setRefundTime(refundInfo.getRefundTime() == null ? "暂未退款"
					: DateTimeUtil.dateToStr(refundInfo.getRefundTime(), "yyyy-MM-dd HH:mm:ss"));
			refundInfoVo.setOrderNo(refundInfo.getOrderNo().toString());
			refundInfoVo.setApplicationTime(DateTimeUtil.dateToStr(refundInfo.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
			if (Integer.parseInt(refundInfo.getStatus()) != Const.RefundStatusEnum.BEING_AUDITED.getCode()) {
				refundInfoVo.setOperationHtml(jobDetails);
			} else {
				refundInfoVo.setOperationHtml(jobDetails + refundsBtn);
			}

			refundList.add(refundInfoVo);
		}
		return refundList;
	}

	/**
	 * 执行退款
	 * 
	 * @param jobId
	 * @return
	 */
	public ServerResponse<String> toRefund(String jobId, HttpServletRequest request) {
		// 查询该任务的退款记录，是否存在，状态是否已退
		SecPublishRefundInfo refundInfo = dao.findFirst(
				"select id,publishUserId,amount,status,payPlatform,orderNo from sec_publish_refund_info where jobId = ?",
				jobId);
		if (refundInfo != null) {
			if (Integer.parseInt(refundInfo.getStatus()) != Const.RefundStatusEnum.Refunded.getCode()) {
				SecPublishPayLog publishPayLog = SecPublishPayLog.dao.findFirst(
						"select publishUserId,status,amount from sec_publish_pay_log where orderNo = ?",
						refundInfo.getOrderNo());
				// 判断订单号是否存在
				if (publishPayLog != null) {
					// 判断支付状态是否为已支付
					if (publishPayLog.getStatus() >= Const.OrderStatusEnum.PAID.getCode()) {
						// 判断订单金额是否一致
						if (publishPayLog.getAmount().setScale(2, BigDecimal.ROUND_HALF_DOWN)
								.equals(refundInfo.getAmount().setScale(2, BigDecimal.ROUND_HALF_DOWN))) {
							// 判断用户是否一致
							if (publishPayLog.getPublishUserId().equals(refundInfo.getPublishUserId())) {
								// 判断该任务支付方式
								if (Integer.parseInt(refundInfo.getPayPlatform()) == Const.PayTypeEnum.ACCOUNTBALANCE
										.getCode()) {
									return refundByBalance(refundInfo.getOrderNo(), refundInfo.getAmount(),
											refundInfo.getPublishUserId(), refundInfo.getId());
								} else if (Integer.parseInt(refundInfo.getPayPlatform()) == Const.PayTypeEnum.ALIPAY
										.getCode()) {
									return refundByAlipay(refundInfo.getOrderNo(), refundInfo.getAmount(),
											refundInfo.getId());
								} else if (Integer.parseInt(refundInfo.getPayPlatform()) == Const.PayTypeEnum.WECHATPAY
										.getCode()) {
									return refundByWechatpay(refundInfo.getOrderNo(), refundInfo.getAmount(),
											refundInfo.getId(), request);
								} else {
									return ServerResponse.createByErrorMsg(Const.ErrorResult.PARAMETER_ERROR);
								}
							}
							return ServerResponse.createByErrorMsg("用户不一致");
						}
						return ServerResponse.createByErrorMsg("金额不一致");
					}
					return ServerResponse.createByErrorMsg("该任务未付款");
				}
				return ServerResponse.createByErrorMsg("该支付订单不存在");
			}
			return ServerResponse.createByErrorMsg("该任务已退款");
		}
		return ServerResponse.createByErrorMsg("该退款记录不存在");
	}

	/**
	 * 查询所有退款人的手机号供搜索提示
	 * 
	 * @return
	 */
	public Set<String> getAllPhone() {
		List<SecPublishRefundInfo> refundList = dao.find("select distinct userPhone from sec_publish_refund_info");
		Set<String> phoneSet = Sets.newHashSet();
		for (SecPublishRefundInfo refundInfo : refundList) {
			if (!StringUtils.isBlank(refundInfo.getUserPhone())) {
				phoneSet.add(refundInfo.getUserPhone());
			}
		}
		return phoneSet;
	}

	/**
	 * 账户余额退款
	 * 
	 * @return
	 */
	private ServerResponse<String> refundByBalance(Long orderNo, BigDecimal amount, String userId, String refundId) {
		// 查询该用户的余额
		SecPublishUser user = SecPublishUser.dao.findFirst("select accountVacancies from sec_publish_user where id = ?",
				userId);
		BigDecimal vacancies = user.getAccountVacancies();
		// 将该用户账户余额加上退款金额
		vacancies = vacancies.add(amount);
		if (Db.update("update sec_publish_user set accountVacancies = ? where id = ?", vacancies, userId) > 0) {
			// 执行修改退款记录状态，添加退款时间
			if (refundSuccess(refundId, new Date()).isSuccess()) {
				return ServerResponse.createBySuccessMsg("退款成功");
			}
		}
		return ServerResponse.createByErrorMsg("退款失败");
	}

	/**
	 * 支付宝退款
	 * 
	 * @return
	 */
	private ServerResponse<String> refundByAlipay(Long orderNo, BigDecimal amount, String refundId) {
		try {
			AlipayClient alipayClient = new DefaultAlipayClient(PropKit.use("alipay.properties").get("open_api_domain"),
					PropKit.use("alipay.properties").get("appid"), PropKit.use("alipay.properties").get("private_key"),
					"JSON", "UTF-8", PropKit.use("alipay.properties").get("alipay_public_key"), "RSA2");
			AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
			SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
			packageParams.put("out_trade_no", orderNo.toString());
			packageParams.put("refund_amount", amount.toString());
			request.setBizContent(JSON.toJSONString(packageParams));
			AlipayTradeRefundResponse response = alipayClient.execute(request);
			if (response.isSuccess()) {
				logger.info("订单：" + orderNo.toString() + "支付宝退款成功");
				// 执行修改退款记录状态，添加退款时间
				Date refundPayTime = response.getGmtRefundPay();
				if (refundSuccess(refundId, refundPayTime).isSuccess()) {
					return ServerResponse.createBySuccessMsg("支付宝退款成功");
				}
			}
			logger.error("订单：" + orderNo.toString() + "支付宝退款失败：" + response.getMsg());
		} catch (AlipayApiException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ServerResponse.createByErrorMsg("支付宝退款失败");
	}

	/**
	 * 微信退款
	 * 
	 * @return
	 */
	private ServerResponse<String> refundByWechatpay(Long orderNo, BigDecimal amount, String refundId,
			HttpServletRequest request) {
		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
		packageParams.put("appid", PropKit.use("wechatpay.properties").get("appid"));
		packageParams.put("mch_id", PropKit.use("wechatpay.properties").get("mchid"));
		String currTime = PayCommonUtil.getCurrTime();
		String strTime = currTime.substring(8, currTime.length());
		String strRandom = PayCommonUtil.buildRandom(4) + "";
		String nonce_str = strTime + strRandom;
		packageParams.put("nonce_str", nonce_str);
		packageParams.put("out_trade_no", orderNo.toString());
		packageParams.put("out_refund_no", refundId);
		String order_price = amount.multiply(new BigDecimal("100")).toString(); // 价格:单位是分
		// 当金额含有小数点时剔除小数点并去掉最后一个零
		if (order_price.contains(".")) {
			int index = order_price.indexOf(".");
			order_price = order_price.replace(".", "");
			order_price = order_price.substring(0, index);
		}
		packageParams.put("total_fee", order_price);
		packageParams.put("refund_fee", order_price);
		String sign = PayCommonUtil.createSign("UTF-8", packageParams, PropKit.use("wechatpay.properties").get("key"));
		packageParams.put("sign", sign);

		// 获得当前目录
		String path = request.getSession().getServletContext().getRealPath("/");
		RefundRequest refundRequest = new RefundRequest();
		Map<String, String> map = Maps.newHashMap();
		try {
			String requestXML = PayCommonUtil.getRequestXml(packageParams);
			String result = refundRequest.httpsRequest(PropKit.use("wechatpay.properties").get("refund_url"),
					requestXML, path);
			map = XMLUtil.doXMLParse(new String(result.toString().getBytes(), "utf-8"));
		} catch (UnrecoverableKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyManagementException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (map.get("return_code").equals(Const.WechatpayResultCode.RESULT_SUCCESS)
				&& map.get("result_code").equals(Const.WechatpayResultCode.RESULT_SUCCESS)) {
			logger.info("订单：" + orderNo.toString() + "微信退款成功");
			if (refundSuccess(refundId, new Date()).isSuccess()) {
				return ServerResponse.createBySuccessMsg("微信退款成功");
			}
		}
		logger.error("订单：" + orderNo.toString() + "微信退款失败：" + map.get("err_code_des"));
		return ServerResponse.createByErrorMsg("微信退款失败");
	}

	/**
	 * 实际退款成功后执行退款记录状态修改
	 * 
	 * @param refundId
	 * @param refundPayTime
	 * @return
	 */
	private ServerResponse<String> refundSuccess(String refundId, Date refundPayTime) {
		// 执行修改退款记录状态，添加退款时间
		int count = Db.update("update sec_publish_refund_info set status = ?, refundTime = ? where id = ?",
				Const.RefundStatusEnum.Refunded.getCode(), refundPayTime, refundId);
		if (count > 0) {
			return ServerResponse.createBySuccess();
		}
		return ServerResponse.createByError();
	}

}
